package com.xf.system.utils.date;


import cn.hutool.core.date.DateField;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import org.apache.commons.lang3.StringUtils;

import java.sql.Timestamp;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.YearMonth;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.util.*;

/**
 * @author xiefengf
 *
 * 时间工具类
 */
public class DateUtils {

    /**
     * 字符串转日期
     * @param dateStr 日期字符串
     * @param pattern 日期格式
     * @return 日期
     */
    public static Date stringToDate(String dateStr, String pattern) {
        try{
            SimpleDateFormat formatter = new SimpleDateFormat(pattern);
            return formatter.parse(dateStr);
        }catch(ParseException e){
            throw new RuntimeException(e);
        }
    }

    /**
     * 日期转字符串
     * @param date 日期
     * @param pattern 日期格式
     * @return 日期字符串
     */
    public static String dateToString(Date date,String pattern){
        try {
            SimpleDateFormat formatter = new SimpleDateFormat(pattern);
            return formatter.format(date);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 将日期对象（Date）转换为字符串类型输出。
     *
     * @param date 日期对象
     * @return String 输出的字符串
     */
    public static String stringFormat(Date date, String format) {
        return new SimpleDateFormat((format)).format(date);
    }

    /**
     * 减去指定的天数 - 当月
     * @param date date
     * @param days 天数，负数为减去
     * @return 增加或减少后的日期对象。例如：如果输入的date是2023-08-10，days为5，则返回的是2023-08
     */
    public static Date addDays(Date date, int days) {
        Calendar cal = Calendar.getInstance();
        cal.setTime(date);
        cal.add(Calendar.DAY_OF_MONTH, days);
        return cal.getTime();
    }

    /**
     * 对指定的日期增加或减少指定的月
     * @param date date
     * @param months months 增加或减少的月数，负数为减少
     * @return 增加或减少后的日期对象。例如：如果输入的date是2023-08-10，months为1，则返回的是2023-09-
     */
    public static Date addMonths(Date date, int months) {
        Calendar cal = Calendar.getInstance();
        cal.setTime(date);
        cal.add(Calendar.MONTH, months);
        return cal.getTime();
    }

    /**
     * 获取月的第一天
     * @param date date
     * @return 月末的最后一天的日期字符串，例如："2023-08-31"
     */
    public static Date getMonthFirstDay(Date date){
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        calendar.set(Calendar.DAY_OF_MONTH,1);
        calendar.add(Calendar.MONTH,0);
        return calendar.getTime();
    }

    /**
     * 获取月的最后一天
     * @param yearMonth 年月格式的字符串，例如："2023-08"
     * @return 月末的最后一天的日期字符串，例如："2023-08-31"
     */
    public static String getMonthLastDay(String yearMonth) {
        int year = Integer.parseInt(yearMonth.split("-")[0]);
        int month = Integer.parseInt(yearMonth.split("-")[1]);
        Calendar cal = Calendar.getInstance();
        cal.set(Calendar.YEAR, year);
        cal.set(Calendar.MONTH, month);
        int lastDay = cal.getMinimum(Calendar.DATE);
        cal.set(Calendar.DAY_OF_MONTH, lastDay - 1);
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
        return sdf.format(cal.getTime());
    }

    /**
     * 获取指定时间前十五分钟的时间
     * @param date 日期
     * @return 日期时间对象，表示指定时间的15分钟前的时间点。例如：如果输入的date是2023-08-10 10:00:0
     */
    public static Date getBeforeMinutes(Date date,Integer minute) {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        calendar.add(Calendar.MINUTE, minute);
        return calendar.getTime();
    }

    /**
     * 获取当前年-月的开始日期
     * 例如：2023-08  --> 2023-08-01 00:00:00
     * @param date 日期对象，表示要获取的开始日期的年月部分。例如：2023-08
     * @return
     */
    public static Date getStartOfMonth(Date date) {
        Calendar calendar = new GregorianCalendar();
        calendar.setTime(date);
        calendar.set(Calendar.DAY_OF_MONTH, 1);
        calendar.set(Calendar.HOUR_OF_DAY, 0);
        calendar.set(Calendar.MINUTE, 0);
        calendar.set(Calendar.SECOND, 0);
        calendar.set(Calendar.MILLISECOND, 0);
        return calendar.getTime();
    }

    /**
     * 获取当前年-月的结束日期
     * 例如：2023-08  --> 2023-08-31 23:59:59
     * @param date 日期对象，表示要获取的结束日期的年月部分。例如：2023-08
     * @return 返回一个日期时间对象，表示指定年月部分的月末的最后一天的23:59:59。例如：如果输入的date是"2023-08"，则返回
     */
    public static Date getEndOfMonth(Date date) {
        Calendar calendar = new GregorianCalendar();
        calendar.setTime(date);
        calendar.set(Calendar.DAY_OF_MONTH, 1);
        calendar.add(Calendar.MONTH, 1);
        calendar.add(Calendar.DAY_OF_MONTH, -1);
        calendar.set(Calendar.HOUR_OF_DAY, 23);
        calendar.set(Calendar.MINUTE, 59);
        calendar.set(Calendar.SECOND, 59);
        calendar.set(Calendar.MILLISECOND, 999);
        return calendar.getTime();
    }

    /**
     * 	一个开始时间（字符串类型） 2023-10 给一个结束时间（字符串类型）2023-09。给出这其中的所有日期
     *	2022-10、2022-11、2022-12、2023-01、2023-02、2023-03、2023-04、2023-05、2023-06、2023-07、2023-08、2023-09
     *
     * @param startYearMonth 开始年月字符串，例如："2023-10"
     * @param endYearMonth 结束年月字符串，例如："2023-09"
     * @return
     */
    public static List<String> getDateRange(String startYearMonth, String endYearMonth) {
        List<String> dateRange = new ArrayList<>();
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM");
        YearMonth startDate = YearMonth.parse(startYearMonth, formatter);
        YearMonth endDate = YearMonth.parse(endYearMonth, formatter);

        while (!startDate.isAfter(endDate)) {
            dateRange.add(startDate.format(formatter));
            startDate = startDate.plusMonths(1);
        }
        return dateRange;
    }

    /**
     * 	一个开始时间（时间类型） 2023-11-01 给一个结束时间（时间类型）2023-11-05。给出这其中的所有日期 【）左闭右开区间
     *	2023-11-01、2023-11-02、2023-11-03、2023-11-04
     * @param startYearMonthDay 开始日期时间对象，例如：2023-11-01 00:00:00
     * @param endYearMonthDay 结束日期时间对象，例如：2023-11-05 23:59:59
     * @return 返回一个日期时间对象列表，包含从开始日期到结束日期之间的所有日期的日期时间。例如：如果输入的startYearMonthDay是2023-11-
     */
    public static List<Date> getDateRange(Date startYearMonthDay, Date endYearMonthDay) {
        // 创建一个列表来存储日期范围
        List<Date> dateRange = new ArrayList<>();
        // 创建 Calendar 对象并设置其时间为开始日期的 00:00:00
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(startYearMonthDay);
        calendar.set(Calendar.HOUR_OF_DAY, 0);
        calendar.set(Calendar.MINUTE, 0);
        calendar.set(Calendar.SECOND, 0);
        calendar.set(Calendar.MILLISECOND, 0);
        // 循环直到当前日期在结束日期之前
        while (calendar.getTime().before(endYearMonthDay)) {
            // 只有当 当前日期小于结束日期时，才将当前日期添加到日期范围列表
//			if (calendar.getTime().compareTo(endYearMonthDay) < 0) {
            dateRange.add(calendar.getTime());
//			}
            // 将 Calendar 对象的日期加一天
            calendar.add(Calendar.DAY_OF_MONTH, 1);
        }
        // 返回日期范围列表
        return dateRange;
    }


    /**
     * 将日期时间的时、分、秒设置为0，将日期时间调整为当天的开始（00:00:00）
     *  2023-09-21 11:45:06 -> 2023-09-21 00:00:00
     * @param date 日期时间对象，表示要调整的日期。例如：2023-09-21 11:45:06
     * @return 返回一个日期时间对象，表示调整后的日期的开始时间的日期。例如：如果输入的date是2023-09-21 11:45:0
     */
    public static Date setToStartDate(Date date) {
        if (date == null) {
            date = new Date();
        }
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        calendar.set(Calendar.HOUR_OF_DAY, 0);
        calendar.set(Calendar.MINUTE, 0);
        calendar.set(Calendar.SECOND, 0);
        calendar.set(Calendar.MILLISECOND, 0);
        return calendar.getTime();
    }

    /**
     * 将日期时间的时、分、秒设置为0，将日期时间调整为当天的结束（23:59:59）
     *  2023-09-21 11:45:06 -> 2023-09-21 23:59:59
     * @param date 日期时间对象，表示要调整的日期。例如：2023-09-21 11:45:06
     * @return 返回一个日期时间对象，表示调整后的日期的结束时间的日期。例如：如果输入的date是2023-09-21 11:45:0
     */
    public static Date setToEndDate(Date date) {
        if (date == null) {
            date = new Date();
        }
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        calendar.set(Calendar.HOUR_OF_DAY, 23);
        calendar.set(Calendar.MINUTE, 59);
        calendar.set(Calendar.SECOND, 59);
        calendar.set(Calendar.MILLISECOND, 0);
        return calendar.getTime();
    }

    /**
     * 计算两个Date之间的年月日差
     * @param date1 比较小的时间
     * @param date2 比较大的时间
     * @return 两个日期之间的天数差，如果date1在date2之前则为负数
     */
    public static long daysBetweenDates(Date date1, Date date2) {
        LocalDate localDate1 = date1.toInstant().atZone(java.time.ZoneId.systemDefault()).toLocalDate();
        LocalDate localDate2 = date2.toInstant().atZone(java.time.ZoneId.systemDefault()).toLocalDate();
        return ChronoUnit.DAYS.between(localDate1, localDate2);
    }

    /**
     * 时间戳转Date
     * @param timestamp 毫秒级时间戳
     * @param format 日期格式
     * @return 日期字符串
     */
    public static String timestampToDateStr(Timestamp timestamp, String format) {
        if (timestamp != null){
            SimpleDateFormat sdf = new SimpleDateFormat(format);
            Date date = new Date(timestamp.getTime());
            return sdf.format(date);
        }
        return null;
    }

    /**
     * 用于比较两个时间字符串的年月部分是否相等
     * @param dateStr1  日期格式为 yyyy-MM
     * @param dateStr2 日期格式为 yyyy-MM
     * @return true 如果两个日期字符串的年月部分相等，否则返回 false
     */
    public static boolean compareYearMonth(String dateStr1, String dateStr2) {
        // 创建日期格式化对象，用于将字符串解析为日期
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM");
        try {
            // 将第一个日期字符串解析为日期对象
            Date date1 = sdf.parse(dateStr1);
            // 将第二个日期字符串解析为日期对象
            Date date2 = sdf.parse(dateStr2);
            // 获取第一个日期对象的年和月部分
            int year1 = Integer.parseInt(new SimpleDateFormat("yyyy").format(date1));
            int month1 = Integer.parseInt(new SimpleDateFormat("MM").format(date1));
            // 获取第二个日期对象的年和月部分
            int year2 = Integer.parseInt(new SimpleDateFormat("yyyy").format(date2));
            int month2 = Integer.parseInt(new SimpleDateFormat("MM").format(date2));
            // 比较两个日期的年和月部分是否相等，并返回结果
            return year1 == year2 && month1 == month2;
        } catch (ParseException e) {
            // 解析失败，返回 false
            return false;
        }
    }


}
